<template>
  <div class="puzzle" :style="{width: width+'px',height: height+'px'}">
    <div class="puzzle__block"
         v-for="(item,index) in blockPoints" :key="item.id"
         @click="handleClick"
         :ref="index === blockPoints.length - 1 ? 'empty' : 'block'"
         :class="index === blockPoints.length - 1 ? 'empty-block' : 'img-block'"
         :data-correctX="correctPoints[index].x"
         :data-correctY="correctPoints[index].y"
         :style="{
           width: blockWidth + 'px',
           height: blockHeight + 'px',
           left: item.x + 'px',
           top: item.y + 'px',
           backgroundImage: `url(${img})`,
           backgroundPosition: `-${correctPoints[index].x}px -${correctPoints[index].y}px`,
           backgroundSize: `${width}px ${height}px`,
         }"></div>
  </div>
</template>

<script>
  export default {
    props: {
      width: {
        type: Number,
        default: 500
      },
      height: {
        type: Number,
        default: 500
      },
      row: {
        type: Number,
        default: 3
      },
      col: {
        type: Number,
        default: 3
      },
      img: {
        type: String,
        required: true
      }
    },
    created() {
      this.getImgWidthHeight(this.img).then(res => {
        if (res.success) {
          let wh = res.data;
          const {width, height} = wh;
          if (width != height) {
            this.$emit('imgWHConfirm');
          }
        } else {
          alert(res.msg);
        }
      })
    },
    computed: {
      blockWidth() {
        return this.width / this.col;
      },
      blockHeight() {
        return this.height / this.row;
      },
      correctPoints() {
        const {row, col, blockWidth, blockHeight} = this;
        let arr = [];
        for (let i = 0; i < row; i++) {
          for (let j = 0; j < col; j++) {
            arr.push({
              id: this.randomStr(2) + Math.random() + +new Date(),
              x: j * blockWidth,
              y: i * blockHeight
            })
          }
        }
        return arr;
      },
      blockPoints() {
        let points = this.correctPoints;
        let length = points.length;
        let lastEle = points[length - 1];
        const copyOfPoints = [...points];
        copyOfPoints.length = length - 1;
        let shuffledPoints = copyOfPoints.sort(() => Math.random() - 0.5);
        shuffledPoints.push(lastEle);
        return shuffledPoints;
      }
    },
    data() {
      return {
        randomSeed: ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm',
          'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 'C', 'V', 'B', 'N', 'M',
          '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
        isStopPlay: false
      }
    },
    methods: {
      async getImgWidthHeight(src) {
        let result = {};
        function imgLoad(src, result) {
          let img = new Image();
          img.src = src;
          return new Promise(resolve => {
            if (img.complete) {
              result.success = true;
              result.data = {
                width: img.width,
                height: img.height
              }
              resolve();
            } else {
              let timeOut = setTimeout(() => {
                result.success = false;
                result.msg = "图片加载失败";
                resolve();
              }, 2000);//2000ms可以认为是超时时间
              img.onload = function () {
                result.success = true;
                result.data = {
                  width: img.width,
                  height: img.height
                }
                window.clearTimeout(timeOut);
                resolve();
              }
            }
          })
        }

        await imgLoad(src, result);
        return result;
      },
      randomStr(len) {
        let arr = this.randomSeed;
        let result = '';
        for (let i = 0; i < len; i++) {
          result += arr[Math.floor(Math.random() * arr.length)];
        }
        return result;
      },
      handleClick(e) {
        const blockDom = e.target;
        const emptyDom = this.$refs.empty[0];
        const {left, top} = blockDom.style;
        if (!this.isAdjacent(blockDom, emptyDom)) {
          return;
        }
        blockDom.style.left = emptyDom.style.left;
        blockDom.style.top = emptyDom.style.top;
        emptyDom.style.left = left;
        emptyDom.style.top = top;
        if (this.isVictory()) {
          this.winGame(emptyDom);
        }
      },
      isAdjacent(blockDom, emptyDom) {
        const {left: blockLeft, top: blockTop} = blockDom.style;
        const {left: emptyLeft, top: emptyTop} = emptyDom.style;
        const {blockWidth, blockHeight} = this;
        const xDis = Math.floor(Math.abs(parseFloat(blockLeft) - parseFloat(emptyLeft)));
        const yDis = Math.floor(Math.abs(parseFloat(blockTop) - parseFloat(emptyTop)));
        return (blockLeft === emptyLeft && yDis === parseInt(blockWidth))
          || (blockTop === emptyTop && xDis === parseInt(blockHeight));
      },
      isVictory() {
        const blockDomArr = this.$refs.block;
        return blockDomArr.every(dom => {
          const {left: domLeft, top: domTop} = dom.style;
          const {correctx: correctX, correcty: correctY} = dom.dataset;
          return parseInt(domLeft) === parseInt(correctX) && parseInt(domTop) === parseInt(correctY);
        })
      },
      winGame(emptyDom) {
        emptyDom.style.opacity = "1";
        setTimeout(() => {
          alert("恭喜您，拼图成功！");
          setTimeout(() => {
            const answerFlag = confirm("要玩下一关嘛？")
            if (answerFlag) {
              this.$emit("nextLevel");
            } else {
              this.isStopPlay = true;
            }
          }, 300);
        }, 500);
      }
    }
  }
</script>

<style>
  .puzzle {
    position: relative;
    border: 2px solid #ccc;
  }

  .puzzle__block {
    box-sizing: border-box;
    position: absolute;
    transition: all .3s;
    border: 1px solid #fff;
  }

  .puzzle__block.img-block {
    cursor: pointer;
  }

  .puzzle__block.empty-block {
    opacity: 0;
  }
</style>